﻿using System;
using System.Collections.Generic;
using System.IO.Ports;
using System.Linq;
using System.Net;
using System.Net.Sockets;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;

namespace SmartPowerHub.Communication.Modbus
{
    public class ModbusRtuClient : ModbusBase, IModbus
    {
        public event DisconnectEvent DisconnectEvent;
        public event ConnectEvent ConnectEvent;

        private SerialPort _serialPort;
        private string _portName;
        private int _baudRate;
        private Parity _parity;
        private int _dataBits;
        private StopBits _stopBits;

        private ModbusRtuHelper modbusRtuHelper;

        public bool IsConnected => _serialPort == null ? false : _serialPort.IsOpen;

        public ModbusRtuClient(string portName, int baudRate = 9600, Parity parity = Parity.Even, int dataBits = 8, StopBits stopBits = StopBits.One, EndianFormat endianFormat = EndianFormat.ABCD, StringFormat stringFormat = StringFormat.UTF8)
        {
            _baudRate = baudRate;
            _parity = parity;
            _stopBits = stopBits;
            _portName = portName;
            _dataBits = dataBits;
            ModbusType = ModbusType.RTU;
            modbusRtuHelper = new ModbusRtuHelper(endianFormat, stringFormat);
        }

        public override async Task ConnectAsync()
        {
            if (_serialPort == null)
            {
                _serialPort = new SerialPort(_portName, _baudRate, _parity, _dataBits, _stopBits)
                {
                    ReadTimeout = 1000,
                    WriteTimeout = 1000,
                    Handshake = Handshake.None,
                    RtsEnable = true
                };
            }
            if (!_serialPort.IsOpen)
            {
                await Task.Run(() => _serialPort.Open());
                ConnectEvent?.Invoke(this);
            }
        }

        public override async Task DisconnectAsync()
        {
            if (_serialPort == null)
                return;
            if (_serialPort.IsOpen)
            {
                await Task.Run(() => _serialPort.Close());
                DisconnectEvent?.Invoke(this);
            }
        }

        protected override async Task<ModbusResponse<T>> SendReadRequestWithReceiveAsync<T>(FunctionCode functionCode, ushort address, ushort count, byte slaveAddress)
        {
            if (!_serialPort.IsOpen)
            {
                await ConnectAsync();
            }
            try
            {
                await _sendLock.WaitAsync(_cancellationTokenSource.Token);
                // 构建请求帧
                byte[] request = modbusRtuHelper.BuildReadRequest(functionCode, address, count, slaveAddress);
                modbusRtuHelper.SendData(_serialPort, request);
                int responseLength = 5 + count * 2; // 响应帧基本长度 + 数据长度
                byte[]? response = await modbusRtuHelper.Receive(_serialPort, responseLength);
                return await modbusRtuHelper.ReadResponse<T>(response, functionCode, count, slaveAddress);
            }
            catch (Exception ex)
            {
                return new ModbusResponse<T>(false, message: ex.Message);
            }
            finally
            {
                _sendLock.Release();
            }
        }

        protected override async Task<ModbusResponse> SendWriteRequestWithReceiveAsync<T>(FunctionCode functionCode, ushort address, T[] values, byte slaveId)
        {
            if (!_serialPort.IsOpen)
            {
                await ConnectAsync();
            }
            try
            {
                await _sendLock.WaitAsync(_cancellationTokenSource.Token);
                List<byte> data = new List<byte>();
                foreach (T value in values)
                {
                    data.AddRange(ByteHelper.GetBytes<T>(value));
                }
                byte[] request = modbusRtuHelper.BuildWriteRequest<T>(functionCode, address, data.ToArray(), slaveId);
                modbusRtuHelper.SendData(_serialPort, request);
                byte[]? response = await modbusRtuHelper.Receive(_serialPort, 8);
                return await modbusRtuHelper.ValidateWriteResponse(response, functionCode, address, slaveId);
            }
            catch (Exception ex)
            {
                return new ModbusResponse(false, message: ex.Message);
            }
            finally
            {
                _sendLock.Release();
            }
        }

        #region 写入

        public async Task<ModbusResponse> WriteMulti<T>(ushort address, T[] values, byte slaveAddress = 1, FunctionCode functionCode = FunctionCode.WriteMultipleRegisters)
        {
            var result = await SendWriteRequestWithReceiveAsync<T>(functionCode, address, values, slaveAddress);
            return result;
        }

        public async Task<ModbusResponse> WriteUInit16(ushort address, ushort value, byte slaveAddress = 1, FunctionCode functionCode = FunctionCode.WriteSingleRegister)
        {
            List<ushort> values = new List<ushort>() { value };
            var result = await SendWriteRequestWithReceiveAsync<ushort>(functionCode, address, values.ToArray(), slaveAddress);
            return result;
        }

        public async Task<ModbusResponse> WriteUInit32(ushort address, uint value, byte slaveAddress = 1, FunctionCode functionCode = FunctionCode.WriteMultipleRegisters)
        {
            List<uint> values = new List<uint>() { value };
            var result = await SendWriteRequestWithReceiveAsync<uint>(functionCode, address, values.ToArray(), slaveAddress);
            return result;
        }

        public async Task<ModbusResponse> WriteS16(ushort address, short value, byte slaveAddress = 1, FunctionCode functionCode = FunctionCode.WriteSingleRegister)
        {
            List<short> values = new List<short>() { value };
            var result = await SendWriteRequestWithReceiveAsync<short>(functionCode, address, values.ToArray(), slaveAddress);
            return result;
        }

        public async Task<ModbusResponse> WriteS32(ushort address, int value, byte slaveAddress = 1, FunctionCode functionCode = FunctionCode.WriteMultipleRegisters)
        {
            List<int> values = new List<int>() { value };
            var result = await SendWriteRequestWithReceiveAsync<int>(functionCode, address, values.ToArray(), slaveAddress);
            return result;
        }

        public async Task<ModbusResponse> WriteDouble(ushort address, double value, byte slaveAddress = 1, FunctionCode functionCode = FunctionCode.WriteMultipleRegisters)
        {
            List<double> values = new List<double>() { value };
            var result = await SendWriteRequestWithReceiveAsync<double>(functionCode, address, values.ToArray(), slaveAddress);
            return result;
        }

        public async Task<ModbusResponse> WriteFloat(ushort address, float value, byte slaveAddress = 1, FunctionCode functionCode = FunctionCode.WriteMultipleRegisters)
        {
            List<float> values = new List<float>() { value };
            var result = await SendWriteRequestWithReceiveAsync<float>(functionCode, address, values.ToArray(), slaveAddress);
            return result;
        }

        public async Task<ModbusResponse> WriteString(ushort address, string value, byte slaveAddress = 1, FunctionCode functionCode = FunctionCode.WriteMultipleRegisters)
        {
            List<string> values = new List<string>() { value };
            var result = await SendWriteRequestWithReceiveAsync<string>(functionCode, address, values.ToArray(), slaveAddress);
            return result;
        }

        #endregion

        #region 读取
        public async Task<ModbusResponse<T>> Read<T>(FunctionCode functionCode, ushort startAddress, ushort count, byte slaveAddress)
        {
            return await SendReadRequestWithReceiveAsync<T>(functionCode, startAddress, count, slaveAddress);
        }

        public async Task<ModbusResponse<ushort>> ReadUInit16(FunctionCode functionCode, ushort startAddress, ushort count = 1, byte slaveAddress = 1)
        {
            var result = await SendReadRequestWithReceiveAsync<ushort>(functionCode, startAddress, count, slaveAddress);
            return result;
        }

        public async Task<ModbusResponse<uint>> ReadUInt32(FunctionCode functionCode, ushort startAddress, ushort count = 2, byte slaveAddress = 1)
        {
            var result = await SendReadRequestWithReceiveAsync<uint>(functionCode, startAddress, count, slaveAddress);
            return result;
        }

        public async Task<ModbusResponse<short>> ReadS16(FunctionCode functionCode, ushort startAddress, ushort count = 1, byte slaveAddress = 1)
        {
            var result = await SendReadRequestWithReceiveAsync<short>(functionCode, startAddress, count, slaveAddress);
            return result;
        }

        public async Task<ModbusResponse<int>> ReadS32(FunctionCode functionCode, ushort startAddress, ushort count = 2, byte slaveAddress = 1)
        {
            var result = await SendReadRequestWithReceiveAsync<int>(functionCode, startAddress, count, slaveAddress);
            return result;
        }

        public async Task<ModbusResponse<double>> ReadDouble(FunctionCode functionCode, ushort startAddress, ushort count = 4, byte slaveAddress = 1)
        {
            var result = await SendReadRequestWithReceiveAsync<double>(functionCode, startAddress, count, slaveAddress);
            return result;
        }

        public async Task<ModbusResponse<float>> ReadFloat(FunctionCode functionCode, ushort startAddress, ushort count = 2, byte slaveAddress = 1)
        {
            var result = await SendReadRequestWithReceiveAsync<float>(functionCode, startAddress, count, slaveAddress);
            return result;
        }

        public async Task<ModbusResponse<string>> ReadString(FunctionCode functionCode, ushort startAddress, ushort count, byte slaveAddress = 1)
        {
            var result = await SendReadRequestWithReceiveAsync<string>(functionCode, startAddress, count, slaveAddress);
            return result;
        }
        #endregion
    }
}
